home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_weapon.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  15.6 KB  |  630 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // g_weapon.c 
  4. // perform the server side effects of a weapon firing
  5.  
  6. #include "g_local.h"
  7.  
  8. static    float    s_quadFactor;
  9. static    vec3_t    forward, right, up;
  10. static    vec3_t    muzzle;
  11.  
  12.  
  13. /*
  14. ======================================================================
  15.  
  16. GAUNTLET
  17.  
  18. ======================================================================
  19. */
  20.  
  21. void Weapon_Gauntlet( gentity_t *ent ) {
  22.  
  23. }
  24.  
  25. /*
  26. ===============
  27. CheckGauntletAttack
  28. ===============
  29. */
  30. qboolean CheckGauntletAttack( gentity_t *ent ) {
  31.     trace_t        tr;
  32.     vec3_t        end;
  33.     gentity_t    *tent;
  34.     gentity_t    *traceEnt;
  35.     int            damage;
  36.  
  37.     // set aiming directions
  38.     AngleVectors (ent->client->ps.viewangles, forward, right, up);
  39.  
  40.     CalcMuzzlePoint ( ent, forward, right, up, muzzle );
  41.  
  42.     VectorMA (muzzle, 32, forward, end);
  43.  
  44.     trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
  45.     if ( tr.surfaceFlags & SURF_NOIMPACT ) {
  46.         return qfalse;
  47.     }
  48.  
  49.     traceEnt = &g_entities[ tr.entityNum ];
  50.  
  51.     // send blood impact
  52.     if ( traceEnt->takedamage && traceEnt->client ) {
  53.         tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
  54.         tent->s.otherEntityNum = traceEnt->s.number;
  55.         tent->s.eventParm = DirToByte( tr.plane.normal );
  56.         tent->s.weapon = ent->s.weapon;
  57.     }
  58.  
  59.     if ( !traceEnt->takedamage) {
  60.         return qfalse;
  61.     }
  62.  
  63.     if (ent->client->ps.powerups[PW_QUAD] ) {
  64.         G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
  65.         s_quadFactor = g_quadfactor.value;
  66.     } else {
  67.         s_quadFactor = 1;
  68.     }
  69.  
  70.     damage = 50 * s_quadFactor;
  71.     G_Damage( traceEnt, ent, ent, forward, tr.endpos,
  72.         damage, 0, MOD_GAUNTLET );
  73.  
  74.     return qtrue;
  75. }
  76.  
  77.  
  78. /*
  79. ======================================================================
  80.  
  81. MACHINEGUN
  82.  
  83. ======================================================================
  84. */
  85.  
  86. /*
  87. ======================
  88. SnapVectorTowards
  89.  
  90. Round a vector to integers for more efficient network
  91. transmission, but make sure that it rounds towards a given point
  92. rather than blindly truncating.  This prevents it from truncating 
  93. into a wall.
  94. ======================
  95. */
  96. void SnapVectorTowards( vec3_t v, vec3_t to ) {
  97.     int        i;
  98.  
  99.     for ( i = 0 ; i < 3 ; i++ ) {
  100.         if ( to[i] <= v[i] ) {
  101.             v[i] = (int)v[i];
  102.         } else {
  103.             v[i] = (int)v[i] + 1;
  104.         }
  105.     }
  106. }
  107.  
  108. #define MACHINEGUN_SPREAD    200
  109. #define    MACHINEGUN_DAMAGE    7
  110. #define    MACHINEGUN_TEAM_DAMAGE    5        // wimpier MG in teamplay
  111.  
  112. void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
  113.     trace_t        tr;
  114.     vec3_t        end;
  115.     float        r;
  116.     float        u;
  117.     gentity_t        *tent;
  118.     gentity_t        *traceEnt;
  119.  
  120.     damage *= s_quadFactor;
  121.  
  122.     r = crandom()*spread;
  123.     u = crandom()*spread;
  124.     VectorMA (muzzle, 8192, forward, end);
  125.     VectorMA (end, r, right, end);
  126.     VectorMA (end, u, up, end);
  127.  
  128.     trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
  129.     if ( tr.surfaceFlags & SURF_NOIMPACT ) {
  130.         return;
  131.     }
  132.  
  133.     traceEnt = &g_entities[ tr.entityNum ];
  134.  
  135.     // snap the endpos to integers, but nudged towards the line
  136.     SnapVectorTowards( tr.endpos, muzzle );
  137.  
  138.     // send bullet impact
  139.     if ( traceEnt->takedamage && traceEnt->client ) {
  140.         tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
  141.         tent->s.eventParm = traceEnt->s.number;
  142.         if( LogAccuracyHit( traceEnt, ent ) ) {
  143.             ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  144.         }
  145.     } else {
  146.         tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
  147.         tent->s.eventParm = DirToByte( tr.plane.normal );
  148.     }
  149.     tent->s.otherEntityNum = ent->s.number;
  150.  
  151.     if ( traceEnt->takedamage) {
  152.         G_Damage( traceEnt, ent, ent, forward, tr.endpos,
  153.             damage, 0, MOD_MACHINEGUN);
  154.     }
  155. }
  156.  
  157.  
  158. /*
  159. ======================================================================
  160.  
  161. BFG
  162.  
  163. ======================================================================
  164. */
  165.  
  166. void BFG_Fire ( gentity_t *ent ) {
  167.     gentity_t    *m;
  168.  
  169.     m = fire_bfg (ent, muzzle, forward);
  170.     m->damage *= s_quadFactor;
  171.     m->splashDamage *= s_quadFactor;
  172.  
  173. //    VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );    // "real" physics
  174. }
  175.  
  176.  
  177. /*
  178. ======================================================================
  179.  
  180. SHOTGUN
  181.  
  182. ======================================================================
  183. */
  184.  
  185. // DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT    are in bg_public.h, because
  186. // client predicts same spreads
  187. #define    DEFAULT_SHOTGUN_DAMAGE    10
  188.  
  189. qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
  190.     trace_t        tr;
  191.     int            damage;
  192.     gentity_t        *traceEnt;
  193.  
  194.     trap_Trace (&tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
  195.     traceEnt = &g_entities[ tr.entityNum ];
  196.  
  197.     // send bullet impact
  198.     if (  tr.surfaceFlags & SURF_NOIMPACT ) {
  199.         return qfalse;
  200.     }
  201.  
  202.     if ( traceEnt->takedamage) {
  203.         damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
  204.  
  205.         G_Damage( traceEnt, ent, ent, forward, tr.endpos,
  206.             damage, 0, MOD_SHOTGUN);
  207.         if( LogAccuracyHit( traceEnt, ent ) ) {
  208.             return qtrue;
  209.         }
  210.     }
  211.     return qfalse;
  212. }
  213.  
  214. // this should match CG_ShotgunPattern
  215. void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
  216.     int            i;
  217.     float        r, u;
  218.     vec3_t        end;
  219.     vec3_t        forward, right, up;
  220.     int            oldScore;
  221.     qboolean    hitClient = qfalse;
  222.  
  223.     // derive the right and up vectors from the forward vector, because
  224.     // the client won't have any other information
  225.     VectorNormalize2( origin2, forward );
  226.     PerpendicularVector( right, forward );
  227.     CrossProduct( forward, right, up );
  228.  
  229.     oldScore = ent->client->ps.persistant[PERS_SCORE];
  230.  
  231.     // generate the "random" spread pattern
  232.     for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
  233.         r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD;
  234.         u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD;
  235.         VectorMA( origin, 8192, forward, end);
  236.         VectorMA (end, r, right, end);
  237.         VectorMA (end, u, up, end);
  238.         if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
  239.             hitClient = qtrue;
  240.             ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  241.         }
  242.     }
  243. }
  244.  
  245.  
  246. void weapon_supershotgun_fire (gentity_t *ent) {
  247.     gentity_t        *tent;
  248.  
  249.     // send shotgun blast
  250.     tent = G_TempEntity( muzzle, EV_SHOTGUN );
  251.     VectorScale( forward, 4096, tent->s.origin2 );
  252.     SnapVector( tent->s.origin2 );
  253.     tent->s.eventParm = rand() & 255;        // seed for spread pattern
  254.     tent->s.otherEntityNum = ent->s.number;
  255.  
  256.     ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
  257. }
  258.  
  259.  
  260. /*
  261. ======================================================================
  262.  
  263. GRENADE LAUNCHER
  264.  
  265. ======================================================================
  266. */
  267.  
  268. void weapon_grenadelauncher_fire (gentity_t *ent) {
  269.     gentity_t    *m;
  270.  
  271.     // extra vertical velocity
  272.     forward[2] += 0.2;
  273.     VectorNormalize( forward );
  274.  
  275.     m = fire_grenade (ent, muzzle, forward);
  276.     m->damage *= s_quadFactor;
  277.     m->splashDamage *= s_quadFactor;
  278.  
  279. //    VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );    // "real" physics
  280. }
  281.  
  282. /*
  283. ======================================================================
  284.  
  285. ROCKET
  286.  
  287. ======================================================================
  288. */
  289.  
  290. void Weapon_RocketLauncher_Fire (gentity_t *ent) {
  291.     gentity_t    *m;
  292.  
  293.     m = fire_rocket (ent, muzzle, forward);
  294.     m->damage *= s_quadFactor;
  295.     m->splashDamage *= s_quadFactor;
  296.  
  297. //    VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );    // "real" physics
  298. }
  299.  
  300.  
  301. /*
  302. ======================================================================
  303.  
  304. PLASMA GUN
  305.  
  306. ======================================================================
  307. */
  308.  
  309. void Weapon_Plasmagun_Fire (gentity_t *ent) {
  310.     gentity_t    *m;
  311.  
  312.     m = fire_plasma (ent, muzzle, forward);
  313.     m->damage *= s_quadFactor;
  314.     m->splashDamage *= s_quadFactor;
  315.  
  316. //    VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );    // "real" physics
  317. }
  318.  
  319. /*
  320. ======================================================================
  321.  
  322. RAILGUN
  323.  
  324. ======================================================================
  325. */
  326.  
  327.  
  328. /*
  329. =================
  330. weapon_railgun_fire
  331. =================
  332. */
  333. #define    MAX_RAIL_HITS    4
  334. void weapon_railgun_fire (gentity_t *ent) {
  335.     vec3_t        end;
  336.     trace_t        trace;
  337.     gentity_t    *tent;
  338.     gentity_t    *traceEnt;
  339.     int            damage;
  340.     int            radiusDamage;
  341.     int            i;
  342.     int            hits;
  343.     int            unlinked;
  344.     gentity_t    *unlinkedEntities[MAX_RAIL_HITS];
  345.  
  346.     damage = 100 * s_quadFactor;
  347.     radiusDamage = 30 * s_quadFactor;
  348.  
  349.     VectorMA (muzzle, 8192, forward, end);
  350.  
  351.     // trace only against the solids, so the railgun will go through people
  352.     unlinked = 0;
  353.     hits = 0;
  354.     do {
  355.         trap_Trace (&trace, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
  356.         if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
  357.             break;
  358.         }
  359.         traceEnt = &g_entities[ trace.entityNum ];
  360.         if ( traceEnt->takedamage ) {
  361.             if( LogAccuracyHit( traceEnt, ent ) ) {
  362.                 hits++;
  363.             }
  364.             G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
  365.         }
  366.         if ( trace.contents & CONTENTS_SOLID ) {
  367.             break;        // we hit something solid enough to stop the beam
  368.         }
  369.         // unlink this entity, so the next trace will go past it
  370.         trap_UnlinkEntity( traceEnt );
  371.         unlinkedEntities[unlinked] = traceEnt;
  372.         unlinked++;
  373.     } while ( unlinked < MAX_RAIL_HITS );
  374.  
  375.     // link back in any entities we unlinked
  376.     for ( i = 0 ; i < unlinked ; i++ ) {
  377.         trap_LinkEntity( unlinkedEntities[i] );
  378.     }
  379.  
  380.     // the final trace endpos will be the terminal point of the rail trail
  381.  
  382.     // snap the endpos to integers to save net bandwidth, but nudged towards the line
  383.     SnapVectorTowards( trace.endpos, muzzle );
  384.  
  385.     // send railgun beam effect
  386.     tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
  387.  
  388.     // set player number for custom colors on the railtrail
  389.     tent->s.clientNum = ent->s.clientNum;
  390.  
  391.     VectorCopy( muzzle, tent->s.origin2 );
  392.     // move origin a bit to come closer to the drawn gun muzzle
  393.     VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
  394.     VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
  395.  
  396.     // no explosion at end if SURF_NOIMPACT, but still make the trail
  397.     if ( trace.surfaceFlags & SURF_NOIMPACT ) {
  398.         tent->s.eventParm = 255;    // don't make the explosion at the end
  399.     } else {
  400.         tent->s.eventParm = DirToByte( trace.plane.normal );
  401.     }
  402.     tent->s.clientNum = ent->s.clientNum;
  403.  
  404.     // give the shooter a reward sound if they have made two railgun hits in a row
  405.     if ( hits == 0 ) {
  406.         // complete miss
  407.         ent->client->accurateCount = 0;
  408.     } else {
  409.         // check for "impressive" reward sound
  410.         ent->client->accurateCount += hits;
  411.         if ( ent->client->accurateCount >= 2 ) {
  412.             ent->client->accurateCount -= 2;
  413.             ent->client->ps.persistant[PERS_REWARD_COUNT]++;
  414.             ent->client->ps.persistant[PERS_REWARD] = REWARD_IMPRESSIVE;
  415.             ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
  416.             // add the sprite over the player's head
  417.             ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET );
  418.             ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
  419.             ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
  420.         }
  421.         ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  422.     }
  423.  
  424. }
  425.  
  426.  
  427. /*
  428. ======================================================================
  429.  
  430. GRAPPLING HOOK
  431.  
  432. ======================================================================
  433. */
  434.  
  435. void Weapon_GrapplingHook_Fire (gentity_t *ent)
  436. {
  437.     if (!ent->client->fireHeld && !ent->client->hook)
  438.         fire_grapple (ent, muzzle, forward);
  439.  
  440.     ent->client->fireHeld = qtrue;
  441. }
  442.  
  443. void Weapon_HookFree (gentity_t *ent)
  444. {
  445.     ent->parent->client->hook = NULL;
  446.     ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
  447.     G_FreeEntity( ent );
  448. }
  449.  
  450. void Weapon_HookThink (gentity_t *ent)
  451. {
  452.     if (ent->enemy) {
  453.         vec3_t v, oldorigin;
  454.  
  455.         VectorCopy(ent->r.currentOrigin, oldorigin);
  456.         v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
  457.         v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
  458.         v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
  459.         SnapVectorTowards( v, oldorigin );    // save net bandwidth
  460.  
  461.         G_SetOrigin( ent, v );
  462.     }
  463.  
  464.     VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
  465. }
  466.  
  467. /*
  468. ======================================================================
  469.  
  470. LIGHTNING GUN
  471.  
  472. ======================================================================
  473. */
  474.  
  475. void Weapon_LightningFire( gentity_t *ent ) {
  476.     trace_t        tr;
  477.     vec3_t        end;
  478.     gentity_t    *traceEnt, *tent;
  479.     int            damage;
  480.  
  481.     damage = 8 * s_quadFactor;
  482.  
  483.     VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
  484.  
  485.     trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
  486.  
  487.     if ( tr.entityNum == ENTITYNUM_NONE ) {
  488.         return;
  489.     }
  490.  
  491.     traceEnt = &g_entities[ tr.entityNum ];
  492.  
  493.     if ( traceEnt->takedamage && traceEnt->client ) {
  494.         tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
  495.         tent->s.otherEntityNum = traceEnt->s.number;
  496.         tent->s.eventParm = DirToByte( tr.plane.normal );
  497.         tent->s.weapon = ent->s.weapon;
  498.         if( LogAccuracyHit( traceEnt, ent ) ) {
  499.             ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  500.         }
  501.     } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
  502.         tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
  503.         tent->s.eventParm = DirToByte( tr.plane.normal );
  504.     }
  505.  
  506.     if ( traceEnt->takedamage) {
  507.         G_Damage( traceEnt, ent, ent, forward, tr.endpos,
  508.             damage, 0, MOD_LIGHTNING);
  509.     }
  510. }
  511.  
  512. //======================================================================
  513.  
  514.  
  515. /*
  516. ===============
  517. LogAccuracyHit
  518. ===============
  519. */
  520. qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
  521.     if( !target->takedamage ) {
  522.         return qfalse;
  523.     }
  524.  
  525.     if ( target == attacker ) {
  526.         return qfalse;
  527.     }
  528.  
  529.     if( !target->client ) {
  530.         return qfalse;
  531.     }
  532.  
  533.     if( !attacker->client ) {
  534.         return qfalse;
  535.     }
  536.  
  537.     if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
  538.         return qfalse;
  539.     }
  540.  
  541.     if ( OnSameTeam( target, attacker ) ) {
  542.         return qfalse;
  543.     }
  544.  
  545.     return qtrue;
  546. }
  547.  
  548.  
  549. /*
  550. ===============
  551. CalcMuzzlePoint
  552.  
  553. set muzzle location relative to pivoting eye
  554. ===============
  555. */
  556. void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
  557.     VectorCopy( ent->s.pos.trBase, muzzlePoint );
  558.     muzzlePoint[2] += ent->client->ps.viewheight;
  559.     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
  560.     // snap to integer coordinates for more efficient network bandwidth usage
  561.     SnapVector( muzzlePoint );
  562. }
  563.  
  564.  
  565.  
  566. /*
  567. ===============
  568. FireWeapon
  569. ===============
  570. */
  571. void FireWeapon( gentity_t *ent ) {
  572.     if (ent->client->ps.powerups[PW_QUAD] ) {
  573.         s_quadFactor = g_quadfactor.value;
  574.     } else {
  575.         s_quadFactor = 1;
  576.     }
  577.  
  578.     // track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
  579.     if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
  580.         ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
  581.     }
  582.  
  583.     // set aiming directions
  584.     AngleVectors (ent->client->ps.viewangles, forward, right, up);
  585.  
  586.     CalcMuzzlePoint ( ent, forward, right, up, muzzle );
  587.  
  588.     // fire the specific weapon
  589.     switch( ent->s.weapon ) {
  590.     case WP_GAUNTLET:
  591.         Weapon_Gauntlet( ent );
  592.         break;
  593.     case WP_LIGHTNING:
  594.         Weapon_LightningFire( ent );
  595.         break;
  596.     case WP_SHOTGUN:
  597.         weapon_supershotgun_fire( ent );
  598.         break;
  599.     case WP_MACHINEGUN:
  600.         if ( g_gametype.integer != GT_TEAM ) {
  601.             Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
  602.         } else {
  603.             Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
  604.         }
  605.         break;
  606.     case WP_GRENADE_LAUNCHER:
  607.         weapon_grenadelauncher_fire( ent );
  608.         break;
  609.     case WP_ROCKET_LAUNCHER:
  610.         Weapon_RocketLauncher_Fire( ent );
  611.         break;
  612.     case WP_PLASMAGUN:
  613.         Weapon_Plasmagun_Fire( ent );
  614.         break;
  615.     case WP_RAILGUN:
  616.         weapon_railgun_fire( ent );
  617.         break;
  618.     case WP_BFG:
  619.         BFG_Fire( ent );
  620.         break;
  621.     case WP_GRAPPLING_HOOK:
  622.         Weapon_GrapplingHook_Fire( ent );
  623.         break;
  624.     default:
  625. // FIXME        G_Error( "Bad ent->s.weapon" );
  626.         break;
  627.     }
  628. }
  629.  
  630.